那麼麻煩的需求,到底要怎麼解決呢?
前一陣子在網路學習平台上看到一個來自於 Functional Programming 的一個很有趣的模型
這裡就叫他為 Either 吧,寫法大概是這樣
public abstract class Either<TLeft,TRight>
{
public abstract Either<TNewLeft, TRight> MapLeft<TNewLeft>(Func<TLeft, TNewLeft> mapping);
public abstract Either<TLeft, TNewRight> MapRight<TNewRight>(Func<TRight, TNewRight> mapping);
public abstract TLeft Reduce(Func<TRight, TLeft> mapping);
}
這個類別的作用類似於 Strategy Pattern
而只是他的決策只有兩個 Left or Right
但這要怎麼使用才會有二選一個效果呢
所以當然不只有這個,現在在把 Left 和 Right 都建立出來吧
public class Left<TLeft, TRight> : Either<TLeft, TRight>
{
TLeft Value { get; }
public Left(TLeft value)
{
this.Value = value;
}
public override Either<TNewLeft, TRight> MapLeft<TNewLeft>(Func<TLeft, TNewLeft> mapping)
=> new Left<TNewLeft, TRight>(mapping(this.Value));
public override Either<TLeft, TNewRight> MapRight<TNewRight>(Func<TRight, TNewRight> mapping)
=> new Left<TLeft, TNewRight>(this.Value);
public override TLeft Reduce(Func<TRight, TLeft> mapping)
=> this.Value;
}
public class Right<TLeft, TRight> : Either<TLeft, TRight>
{
TRight Value { get; }
public Right(TRight value)
{
this.Value = value;
}
public override Either<TNewLeft, TRight> MapLeft<TNewLeft>(Func<TLeft, TNewLeft> mapping)
=> new Right<TNewLeft, TRight>(this.Value);
public override Either<TLeft, TNewRight> MapRight<TNewRight>(Func<TRight, TNewRight> mapping)
=> new Right<TLeft, TNewRight>(mapping(this.Value));
public override TLeft Reduce(Func<TRight, TLeft> mapping)
=> mapping(this.Value);
}
所以實際上是用建構子所帶入的參數來決定實際上被使用的類別
所以實際上的用法大概像是這樣
public abstract class MyChoose {
public Either<LeftType, RightType> Run(object val)
{
if(val is RightType right)
return new Right(right);
else (val is LeftType left)
return new Left(left);
// 此處因為示範緣故,未定義空物件,所以單以名稱表示
return Left.Empty;
}
}
而最後回傳結果的用法是
string result = either.MapLeft(left => left.ToString())
.Reduce(right => right.ToString());
解釋一下為什麼這樣就可以正確取得我們所要的結果
當我們使用 Left 的時候呼叫 MapLeft 會確實讓我們帶入的參數 Left.ToString() 被執行
並且將結果成為新的 Either<string,RightType> 的建構子參數來建立新的 Either 物件
而如果呼叫 MapRight 時,只做把原本的 LeftType 放進新的 Either<LeftType,TNewRight>
並不會去呼叫參數的方法
所以這時候就會發生無論 MapRight 怎麼呼叫都不會改變實際的結果
反之 Right 也是只處理 RightType 的進展,同時忽略 Left 相關的所有方法
而最後 Reduce 則是將 Right 型別變成 Left 的型別的物件的目的是為了取得最終結果
以上述的範例,如果想要取得字串結果,那我就先把左邊變成字串
最後在 Reduce 把右邊也變成字串
那最後的結果就一定會得到字串
這好像挺接近我們想要的結果
補充優點
可能適用的方法已經有了,接下來如何將這個解決方案變成我們所期望的結果
就讓我們繼續看下去吧